/*
 * Copyright 2024, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */

#include <KernelExport.h>
#include <PCI.h>
#include <OS.h>
#include <graphic_driver.h>
#include <malloc.h>
#include <string.h>
#include <stdio.h>

#include "loongarch_vesa_private.h"

#define TRACE_DEVICE
#ifdef TRACE_DEVICE
#define TRACE(x...) dprintf(DRIVER_PREFIX ": " x)
#else
#define TRACE(x...) ;
#endif

#define ERROR(x...) dprintf(DRIVER_PREFIX ": " x)

// 外部变量
extern pci_module_info *gPCI;
device_hooks gDeviceHooks = {
    device_open,
    device_close,
    device_free,
    device_ioctl,
    device_read,
    device_write,
    NULL,
    NULL,
    NULL,
    NULL};

// 映射设备寄存器和帧缓冲
static status_t
map_device_memory(device_info *di)
{
    char buffer[B_OS_NAME_LENGTH];
    loongarch_vesa_shared_info *si = di->si;
    pci_info *pcii = &di->pcii;

    // 使能PCI内存访问和总线主控
    uint16 command = gPCI->read_pci_config(pcii->bus, pcii->device, pcii->function, PCI_command, 2);
    command |= PCI_command_memory | PCI_command_master;
    gPCI->write_pci_config(pcii->bus, pcii->device, pcii->function, PCI_command, 2, command);

    // 映射寄存器空间
    sprintf(buffer, DEVICE_FORMAT " regs",
            pcii->vendor_id, pcii->device_id,
            pcii->bus, pcii->device, pcii->function);

    // 寄存器通常在BAR0
    si->regs_area = map_physical_memory(
        buffer,
        pcii->u.h0.base_registers_pci[0],
        pcii->u.h0.base_register_sizes[0],
        B_ANY_KERNEL_ADDRESS,
        B_READ_AREA | B_WRITE_AREA,
        (void **)&di->regs);

    if (si->regs_area < 0)
    {
        ERROR("Failed to map registers\n");
        return si->regs_area;
    }

    // 映射帧缓冲空间
    sprintf(buffer, DEVICE_FORMAT " framebuffer",
            pcii->vendor_id, pcii->device_id,
            pcii->bus, pcii->device, pcii->function);

    // 帧缓冲通常在BAR1
    si->area = map_physical_memory(
        buffer,
        pcii->u.h0.base_registers_pci[1],
        pcii->u.h0.base_register_sizes[1],
        B_ANY_KERNEL_BLOCK_ADDRESS | B_WRITE_COMBINING_MEMORY,
        B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA,
        (void **)&si->frame_buffer);

    // 如果写组合内存映射失败，尝试普通映射
    if (si->area < 0)
    {
        si->area = map_physical_memory(
            buffer,
            pcii->u.h0.base_registers_pci[1],
            pcii->u.h0.base_register_sizes[1],
            B_ANY_KERNEL_BLOCK_ADDRESS,
            B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA,
            (void **)&si->frame_buffer);
    }

    if (si->area < 0)
    {
        ERROR("Failed to map framebuffer\n");
        delete_area(si->regs_area);
        si->regs_area = -1;
        return si->area;
    }

    // 保存帧缓冲物理地址和大小
    si->physical_frame_buffer = pcii->u.h0.base_registers_pci[1];
    si->frame_buffer_size = pcii->u.h0.base_register_sizes[1];

    // 映射光标缓冲区
    sprintf(buffer, DEVICE_FORMAT " cursor",
            pcii->vendor_id, pcii->device_id,
            pcii->bus, pcii->device, pcii->function);

    // 分配光标缓冲区（通常为64x64像素，ARGB格式）
    si->cursor_buffer_size = 64 * 64 * 4;
    si->cursor_area = create_area(
        buffer,
        (void **)&si->cursor_buffer,
        B_ANY_KERNEL_ADDRESS,
        ROUND_TO_PAGE_SIZE(si->cursor_buffer_size),
        B_FULL_LOCK,
        B_READ_AREA | B_WRITE_AREA);

    if (si->cursor_area < 0)
    {
        ERROR("Failed to create cursor buffer\n");
        delete_area(si->area);
        delete_area(si->regs_area);
        si->area = -1;
        si->regs_area = -1;
        return si->cursor_area;
    }

    // 获取光标缓冲区物理地址
    physical_entry pe;
    if (get_memory_map(si->cursor_buffer, si->cursor_buffer_size, &pe, 1) != B_OK)
    {
        ERROR("Failed to get cursor buffer physical address\n");
        delete_area(si->cursor_area);
        delete_area(si->area);
        delete_area(si->regs_area);
        si->cursor_area = -1;
        si->area = -1;
        si->regs_area = -1;
        return B_ERROR;
    }

    si->physical_cursor_buffer = pe.address;

    // 初始化硬件参数
    si->cursor_format = B_RGBA32;
    si->cursor_width = 64;
    si->cursor_height = 64;
    si->cursor_hot_x = 0;
    si->cursor_hot_y = 0;
    si->cursor_visible = false;
    si->cursor_x = 0;
    si->cursor_y = 0;

    // 读取硬件版本
    si->revision = RREG32(LS7A_DC_REVISION);

    // 检查是否支持硬件光标
    si->hardware_cursor = true;

    TRACE("Device mapped successfully: regs=%p, fb=%p, cursor=%p\n",
          di->regs, si->frame_buffer, si->cursor_buffer);
    TRACE("Framebuffer: phys=0x%lx, size=%lu\n",
          si->physical_frame_buffer, si->frame_buffer_size);
    TRACE("Hardware revision: 0x%lx\n", si->revision);

    return B_OK;
}

// 取消映射设备内存
static void
unmap_device_memory(device_info *di)
{
    loongarch_vesa_shared_info *si = di->si;

    if (si->cursor_area >= 0)
        delete_area(si->cursor_area);

    if (si->area >= 0)
        delete_area(si->area);

    if (si->regs_area >= 0)
        delete_area(si->regs_area);

    si->cursor_area = -1;
    si->area = -1;
    si->regs_area = -1;
}

// 初始化硬件光标
static status_t
init_hardware_cursor(device_info *di)
{
    loongarch_vesa_shared_info *si = di->si;

    // 设置光标地址
    WREG32(LS7A_DC_CURSOR_ADDR, (uint32)si->physical_cursor_buffer);

    // 设置光标颜色（ARGB格式）
    WREG32(LS7A_DC_CURSOR_FG, 0xFFFFFFFF); // 白色
    WREG32(LS7A_DC_CURSOR_BG, 0x00000000); // 黑色

    // 初始化光标位置
    WREG32(LS7A_DC_CURSOR_POS, (si->cursor_y << 16) | si->cursor_x);

    // 清空光标缓冲区
    memset(si->cursor_buffer, 0, si->cursor_buffer_size);

    return B_OK;
}

// 读取EDID数据
static status_t
read_edid(device_info *di)
{
    loongarch_vesa_shared_info *si = di->si;
    bool has_edid = false;
    uint8 checksum = 0;

    // 读取EDID数据（128字节）
    for (int i = 0; i < 128; i++)
    {
        si->edid[i] = RREG32(LS7A_DC_EDID_START + i * 4) & 0xFF;
        checksum += si->edid[i];
    }

    // 验证EDID头部标识（前8字节应该是固定的0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00）
    if (si->edid[0] == 0x00 && si->edid[1] == 0xFF && si->edid[2] == 0xFF && si->edid[3] == 0xFF &&
        si->edid[4] == 0xFF && si->edid[5] == 0xFF && si->edid[6] == 0xFF && si->edid[7] == 0x00)
    {
        // 验证校验和（所有字节加起来应该是0）
        if (checksum == 0)
        {
            has_edid = true;
            TRACE("Valid EDID data found\n");

            // 打印显示器信息
            char manufacturer[4];
            manufacturer[0] = 'A' + ((si->edid[8] & 0x7C) >> 2) - 1;
            manufacturer[1] = 'A' + (((si->edid[8] & 0x03) << 3) | ((si->edid[9] & 0xE0) >> 5)) - 1;
            manufacturer[2] = 'A' + (si->edid[9] & 0x1F) - 1;
            manufacturer[3] = 0;

            uint16 product_id = si->edid[10] | (si->edid[11] << 8);
            uint32 serial = si->edid[12] | (si->edid[13] << 8) | (si->edid[14] << 16) | (si->edid[15] << 24);

            TRACE("Monitor: %s, Product ID: %u, Serial: %lu\n", manufacturer, product_id, serial);
        }
        else
        {
            ERROR("EDID checksum invalid\n");
        }
    }
    else
    {
        ERROR("EDID header invalid\n");
    }

    si->has_edid = has_edid;
    return has_edid ? B_OK : B_ERROR;
}

// 字节序转换函数 - 处理大端帧缓冲的字节序转换
static void
swap_bytes_if_needed(loongarch_vesa_shared_info *si)
{
    // 检查是否需要进行字节序转换
    // 龙芯CPU是小端，但某些配置下帧缓冲可能是大端格式
    // 这里我们通过检查特定寄存器位来确定是否需要转换

    // 假设配置寄存器的某个位指示字节序
    bool need_swap = (RREG32(LS7A_DC_CONFIG) & 0x80000000) != 0;

    if (!need_swap)
        return;

    TRACE("Byte swapping enabled for framebuffer\n");

    // 根据颜色深度执行不同的字节序转换
    switch (si->bits_per_pixel)
    {
    case 16:
    {
        // 16位色深 - 交换每个像素的高低字节
        uint16 *fb16 = (uint16 *)si->frame_buffer;
        for (uint32 i = 0; i < si->frame_buffer_size / 2; i++)
        {
            fb16[i] = (fb16[i] >> 8) | (fb16[i] << 8);
        }
        break;
    }
    case 24:
    {
        // 24位色深 - 交换RGB顺序
        uint8 *fb8 = (uint8 *)si->frame_buffer;
        for (uint32 i = 0; i < si->frame_buffer_size; i += 3)
        {
            uint8 temp = fb8[i];
            fb8[i] = fb8[i + 2];
            fb8[i + 2] = temp;
        }
        break;
    }
    case 32:
    {
        // 32位色深 - 交换RGBA顺序
        uint32 *fb32 = (uint32 *)si->frame_buffer;
        for (uint32 i = 0; i < si->frame_buffer_size / 4; i++)
        {
            fb32[i] = ((fb32[i] & 0xFF) << 24) |
                      ((fb32[i] & 0xFF00) << 8) |
                      ((fb32[i] & 0xFF0000) >> 8) |
                      ((fb32[i] & 0xFF000000) >> 24);
        }
        break;
    }
    }
}

// 初始化显示控制器
static status_t
init_display_controller(device_info *di)
{
    loongarch_vesa_shared_info *si = di->si;

    // 复位显示控制器
    WREG32(LS7A_DC_RESET, 1);
    snooze(10000); // 等待10ms
    WREG32(LS7A_DC_RESET, 0);

    // 设置默认显示模式（1024x768@60Hz，32位色深）
    si->width = 1024;
    si->height = 768;
    si->bits_per_pixel = 32;
    si->bytes_per_row = si->width * (si->bits_per_pixel / 8);
    si->color_space = B_RGB32;
    si->current_mode = DISPLAY_MODE_1024_768_60HZ;

    // 设置帧缓冲地址
    WREG32(LS7A_DC_FB_ADDR, (uint32)si->physical_frame_buffer);

    // 设置帧缓冲行跨度
    WREG32(LS7A_DC_FB_STRIDE, si->bytes_per_row);

    // 设置帧缓冲原点
    WREG32(LS7A_DC_FB_ORIGIN, 0);

    // 设置水平显示参数
    // 1024x768@60Hz: 总像素=1344, 同步=136, 前肩=24, 后肩=160
    WREG32(LS7A_DC_HDISPLAY, (1024 - 1) | ((1344 - 1) << 16));
    WREG32(LS7A_DC_HSYNC, (1024 + 24) | ((1024 + 24 + 136) << 16));

    // 设置垂直显示参数
    // 1024x768@60Hz: 总行数=806, 同步=6, 前肩=3, 后肩=29
    WREG32(LS7A_DC_VDISPLAY, (768 - 1) | ((806 - 1) << 16));
    WREG32(LS7A_DC_VSYNC, (768 + 3) | ((768 + 3 + 6) << 16));

    // 设置配置寄存器
    uint32 config = LS7A_DC_CONFIG_FORMAT_32BPP | // 32位每像素
                    LS7A_DC_CONFIG_HSYNC_POL |    // 水平同步极性
                    LS7A_DC_CONFIG_VSYNC_POL;     // 垂直同步极性

    // 初始化硬件光标
    init_hardware_cursor(di);

    // 读取EDID数据
    read_edid(di);

    // 使能显示控制器和硬件光标
    config |= LS7A_DC_CONFIG_ENABLE | LS7A_DC_CONFIG_CURSOR;
    WREG32(LS7A_DC_CONFIG, config);

    // 设置DPMS模式为开启
    si->dpms_mode = B_DPMS_ON;

    return B_OK;
}

// 初始化设备
status_t
device_init(device_info *di)
{
    // 创建共享信息区域
    char buffer[B_OS_NAME_LENGTH];
    sprintf(buffer, DEVICE_FORMAT " shared",
            di->pcii.vendor_id, di->pcii.device_id,
            di->pcii.bus, di->pcii.device, di->pcii.function);

    // 创建共享区域
    di->shared_area = create_area(buffer,
                                  (void **)&di->si, B_ANY_KERNEL_ADDRESS,
                                  ROUND_TO_PAGE_SIZE(sizeof(loongarch_vesa_shared_info)),
                                  B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA);

    if (di->shared_area < 0)
    {
        ERROR("Failed to create shared area\n");
        return di->shared_area;
    }

    // 初始化共享信息
    memset(di->si, 0, sizeof(loongarch_vesa_shared_info));
    di->si->area = di->shared_area;
    di->si->magic = LOONGARCH_VESA_PRIVATE_DATA_MAGIC;

    // 映射设备内存
    status_t status = map_device_memory(di);
    if (status != B_OK)
    {
        ERROR("Failed to map device memory\n");
        delete_area(di->shared_area);
        di->shared_area = -1;
        di->si = NULL;
        return status;
    }

    // 初始化显示控制器
    status = init_display_controller(di);
    if (status != B_OK)
    {
        ERROR("Failed to initialize display controller\n");
        unmap_device_memory(di);
        delete_area(di->shared_area);
        di->shared_area = -1;
        di->si = NULL;
        return status;
    }

    TRACE("Device initialized successfully\n");
    return B_OK;
}

// 卸载设备
void device_uninit(device_info *di)
{
    TRACE("device_uninit()\n");

    if (di->si != NULL)
    {
        // 关闭显示控制器
        WREG32(LS7A_DC_CONFIG, 0);

        // 取消映射设备内存
        unmap_device_memory(di);

        // 删除共享区域
        delete_area(di->shared_area);
        di->shared_area = -1;
        di->si = NULL;
    }
}

// 打开设备
status_t
device_open(const char *name, uint32 flags, void **cookie)
{
    TRACE("device_open()\n");

    // 查找设备
    int32 index = 0;
    while (index < MAX_LOONGARCH_VESA_DEVICES && gDeviceInfo[index] != NULL)
    {
        if (!strcmp(name, gDeviceNames[index]))
            break;
        index++;
    }

    if (index >= MAX_LOONGARCH_VESA_DEVICES || gDeviceInfo[index] == NULL)
        return B_BAD_VALUE;

    device_info *di = gDeviceInfo[index];
    *cookie = di;

    return B_OK;
}

// 关闭设备
status_t
device_close(void *cookie)
{
    TRACE("device_close()\n");
    return B_OK;
}

// 释放设备
status_t
device_free(void *cookie)
{
    TRACE("device_free()\n");
    return B_OK;
}

// 读取设备
status_t
device_read(void *cookie, off_t pos, void *buffer, size_t *length)
{
    *length = 0;
    return B_NOT_ALLOWED;
}

// 写入设备
status_t
device_write(void *cookie, off_t pos, const void *buffer, size_t *length)
{
    *length = 0;
    return B_NOT_ALLOWED;
}

// 设备控制
status_t
device_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
{
    device_info *di = (device_info *)cookie;
    loongarch_vesa_shared_info *si = di->si;

    switch (op)
    {
    case LOONGARCH_VESA_GET_DEVICE_INFO:
        // 返回设备信息
        if (length != sizeof(loongarch_vesa_shared_info))
            return B_BAD_VALUE;
        memcpy(buffer, si, sizeof(loongarch_vesa_shared_info));
        return B_OK;

    case LOONGARCH_VESA_GET_EDID:
        // 返回EDID数据
        if (length != 128)
            return B_BAD_VALUE;
        if (!si->has_edid)
            return B_ERROR;
        memcpy(buffer, si->edid, 128);
        return B_OK;

    case LOONGARCH_VESA_GET_FRAME_BUFFER_CONFIG:
        // 返回帧缓冲配置
        if (length != sizeof(frame_buffer_config))
            return B_BAD_VALUE;
        {
            frame_buffer_config *config = (frame_buffer_config *)buffer;
            config->frame_buffer = si->physical_frame_buffer;
            config->frame_buffer_dma = 0;
            config->bytes_per_row = si->bytes_per_row;
            config->width = si->width;
            config->height = si->height;
            config->depth = si->bits_per_pixel;
            config->color_space = si->color_space;
        }
        return B_OK;

        // 其他IOCTL命令...
    }

    return B_DEV_INVALID_IOCTL;
}